﻿using System;
using System.Runtime.InteropServices;

namespace DetoursNET
{
    internal static class Native
    {
        private static readonly bool IsX64 = IntPtr.Size == 8;

        public static long DetourTransactionBegin()
        {
            if (IsX64)
            {
                return Native_x64.DetourTransactionBeginWrapper();
            }
            else
            {
                return Native_x86.DetourTransactionBeginWrapper();
            }
        }

        public static long DetourTransactionAbort()
        {
            if (IsX64)
            {
                return Native_x64.DetourTransactionAbortWrapper();
            }
            else
            {
                return Native_x86.DetourTransactionAbortWrapper();
            }
        }

        public static long DetourTransactionCommit()
        {
            if (IsX64)
            {
                return Native_x64.DetourTransactionCommitWrapper();
            }
            else
            {
                return Native_x86.DetourTransactionCommitWrapper();
            }
        }

        public static long DetourUpdateThread(IntPtr hThread)
        {
            if (IsX64)
            {
                return Native_x64.DetourUpdateThreadWrapper(hThread);
            }
            else
            {
                return Native_x86.DetourUpdateThreadWrapper(hThread);
            }
        }

        public static long DetourAttach(ref IntPtr ppPointer, IntPtr pDetour)
        {
            if (IsX64)
            {
                return Native_x64.DetourAttachWrapper(ref ppPointer, pDetour);
            }
            else
            {
                return Native_x86.DetourAttachWrapper(ref ppPointer, pDetour);
            }
        }

        public static long DetourDetach(ref IntPtr ppPointer, IntPtr pDetour)
        {
            if (IsX64)
            {
                return Native_x64.DetourDetachWrapper(ref ppPointer, pDetour);
            }
            else
            {
                return Native_x86.DetourDetachWrapper(ref ppPointer, pDetour);
            }
        }

        public static class WinApi
        {
            [DllImport("kernel32", ExactSpelling = true, SetLastError = true)]
            public static extern IntPtr GetCurrentThread();

            [DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
            public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

            [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)]
            public static extern IntPtr GetModuleHandle([Optional] string lpModuleName);
        }

        private static class Native_x64
        {
            private const string DllName = "DetoursWrapper.x64.dll";

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionBeginWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionAbortWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionCommitWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourUpdateThreadWrapper(IntPtr hThread);

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourAttachWrapper(ref IntPtr ppPointer, IntPtr pDetour);

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourDetachWrapper(ref IntPtr ppPointer, IntPtr pDetour);
        }

        private static class Native_x86
        {
            private const string DllName = "DetoursWrapper.x86.dll";

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionBeginWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionAbortWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourTransactionCommitWrapper();

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourUpdateThreadWrapper(IntPtr hThread);

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourAttachWrapper(ref IntPtr ppPointer, IntPtr pDetour);

            [DllImport(DllName, SetLastError = true)]
            public static extern long DetourDetachWrapper(ref IntPtr ppPointer, IntPtr pDetour);
        }
    }
}